package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.math.ec.ECPoint;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {

    @Autowired
    ApArticleMapper apArticleMapper;
    @Override
    public void computeHotArticle() {
    //1.查询近五天的文章
        // 1.1  得到5天前的时间
        String params = LocalDateTime.now().minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        //2.计算每篇文章的热度值 将文章封装成vo集合
        // 1.2  调用articleMapper查询
        List<ApArticle> apArticles = apArticleMapper.selectArticleByDate(params);
        if (CollectionUtils.isEmpty(apArticles)){
            return;
        }
       List<HotArticleVo>  hotArticleVoList = computeArticleScore(apArticles);
        //3.按照频道 每个频道下缓存热度最高的30条文章
        cacheTagToRedis(hotArticleVoList);
    }

    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
    //1.根据 文章id查询出文章数据
        ApArticle article = apArticleMapper.selectById(mess.getArticleId());
        if (article==null){
            return;
        }
        //2.更新文章 各个行为数据
        int newComment = (int)(article.getComment()==null?0:article.getComment() + mess.getComment());
        article.setComment(newComment<0 ? 0:newComment);

        int newLike = (int)(article.getLikes() == null ? 0 : article.getLikes() + mess.getLike());
        article.setLikes(newLike < 0 ? 0 : newLike);

        int newView = (int) (article.getViews() == null ? 0 : article.getViews() + mess.getView());
        article.setViews(newView<0 ? 0 : newView);

        int newCollect = (int) (article.getCollection() == null ? 0 : article.getCollection() + mess.getCollect());
        article.setCollection(newCollect<0 ? 0 : newCollect);

        apArticleMapper.updateById(article);

    //3.计算文章得分
        Integer score = computeScore(article);
        //4.判断文章是否是今日发布
        String nowStr = DateUtils.dateToString(new Date());//今日时间
        String publishStr =DateUtils.dateToString(article.getPublishTime());//发布时间
        if (nowStr.equals(publishStr)){
           score =score*3;
        }
    //5.查询对应频道热点文章，替换分值较低的文章
        updateArticleCache(article,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + article.getChannelId());
    //6.查询推荐频道热点文章，替换分值较低文章
        updateArticleCache(article,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ ArticleConstants.DEFAULT_TAG);
    }

    /**
     * 查询对应频道热点文章，替换分值较低的文章
     * @param article 当前文章数据
     * @param score 该文章最新得分
     * @param cacheKey 要更新缓存的key
     */
    private void updateArticleCache(ApArticle article, Integer score, String cacheKey) {
        //1.获取redis中热点文章的缓存数据
        ValueOperations<String, String> valueOper = redisTemplate.opsForValue();
        String articleVoListJson = valueOper.get(cacheKey);
        if (StringUtils.isNotBlank(articleVoListJson)){
            boolean isHas=false;
            //2.判断当前文章是否已经存在于热点文章当中
            //如果存在直接更新热度值
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(articleVoListJson, HotArticleVo.class);
            for (HotArticleVo articleVo : hotArticleVoList) {
                if (articleVo.getId().equals(article.getId())){
                    articleVo.setScore(score);//重新设置得分
                    break;
                }
            }
            //3.如果不存在 直接将文章封装成vo对象 加入到热点文章集合中
            if (!isHas){
                HotArticleVo articleVo=new HotArticleVo();
                BeanUtils.copyProperties(article,articleVo);
                articleVo.setScore(score);
                hotArticleVoList.add(articleVo);
            }
            //4.重新对热点文章进行排序 并截图前30条热点文章
          hotArticleVoList = hotArticleVoList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            //5.缓存到redis中
            valueOper.set(cacheKey,JSON.toJSONString(hotArticleVoList));
        }



    }

    @Autowired
    AdminFeign adminFeign;

    /** 按照频道缓存 文章
     *  按照热度降序排序 每个频道 只能缓存热度最高的30条文章
     *  推荐频道 缓存所有的文章 热度最高的30条文章
     * @param hotArticleVoList
     */
    private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
        // 1. 远程查询频道列表
        ResponseResult<List<AdChannel>> channlResult= adminFeign.selectChannels();
        if (!channlResult.checkCode()){
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,"远程调用失败");
        }
        // 2. 遍历频道，  在所有文章中挑出该频道的文章  调用保存方法 保存热点文章
        List<AdChannel> channelList = channlResult.getData();
        for (AdChannel channel : channelList) {
            List<HotArticleVo> hotArticleVos = hotArticleVoList.stream()
                    .filter(hotArticle->hotArticle.getChannelId().equals(channel.getId()))
                    .collect(Collectors.toList());
            sortAndCache(hotArticleVos,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+channel.getId());
        }
        // 3. 缓存推荐频道    调用保存放 保存热点文章
        sortAndCache(hotArticleVoList,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);
    };
    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * 将热点文章进行缓存 截图前30条 缓存到redis
     * @param articleVoList
     * @param cacheKey
     */
    private void sortAndCache(List<HotArticleVo> articleVoList, String cacheKey) {
        List<HotArticleVo> hotArticleVos = articleVoList.stream()
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
       // 将30条文章 缓存到redis
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleVos));
    }




    /**
     * 将文章封装成vo对象
     * @param apArticles
     * @return
     */
    private List<HotArticleVo> computeArticleScore(List<ApArticle> apArticles) {
        //1.循环文章列表
//       return   apArticles.stream().map(apArticle -> {
//            HotArticleVo articleVo= new HotArticleVo();
//            BeanUtils.copyProperties(apArticle,articleVo);
//        Integer score  = computeScore(apArticle);
//       //2.计算每篇文章的得分
//            //3.将article封装成vo对象
//        articleVo.setScore(score);
//        return articleVo;
//        }).collect(Collectors.toList());

        return apArticles.stream().map(new Function<ApArticle, HotArticleVo>() {
            @Override
            public HotArticleVo apply(ApArticle apArticle) {
                HotArticleVo articleVo = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, articleVo);
                Integer score = computeScore(apArticle);
                //2.计算每篇文章的得分
                //3.将article封装成vo对象
                articleVo.setScore(score);
                return articleVo;
            }
        }).collect(Collectors.toList());

//        List<HotArticleVo> hotArticleVoList = new ArrayList<>();
//        for (ApArticle apArticle : apArticles) {
//            HotArticleVo articleVo= new HotArticleVo();
//            BeanUtils.copyProperties(apArticle,articleVo);
//            Integer score  = computeScore(apArticle);
//        }
//        return hotArticleVoList;
    }

    /**
     * 计算每篇文章的得分
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        //按照权重计算得分
        Integer score = 0;
        if (apArticle.getViews()!=null){
            score += apArticle.getViews()* ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        if (apArticle.getLikes()!=null){
            score += apArticle.getLikes()* ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getComment()!=null){
            score += apArticle.getComment()* ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getCollection()!=null){
            score += apArticle.getCollection()* ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}
